#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
WebDAV云同步模块
支持上传和下载数据库文件到WebDAV服务器
"""

import os
import sys
import base64
import requests
from urllib.parse import urljoin, urlparse
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QGridLayout, 
                            QLabel, QLineEdit, QPushButton, QApplication, 
                            QProgressBar, QMessageBox, QMainWindow, QWidget,
                            QListWidget, QListWidgetItem, QDateTimeEdit, QCheckBox,
                            QMenu, QAction, QSplitter, QFrame, QScrollArea, QSizePolicy,
                            QFileDialog, QTabWidget, QToolButton, QGroupBox, QFormLayout,
                            QDialogButtonBox, QTextEdit, QComboBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSettings
from PyQt5.QtGui import QIcon

def get_machine_code():
    """获取本机特征码，用于在数据库名称中区分不同用户"""
    import socket
    mac = hex(abs(hash(socket.gethostname())))[2:]
    return mac[:8]  # 取前8位作为机器码

def get_user_db_path():
    """获取基于用户名的数据库文件路径"""
    # 从设置中获取当前登录的用户名
    settings = QSettings("TodoApp", "Settings")
    username = settings.value("current_username", "")
    
    if not username:
        username = "default"
    
    if getattr(sys, 'frozen', False):
        # 打包环境
        app_dir = os.path.dirname(sys.executable)
        return os.path.join(app_dir, f"{username}_todo_data.db")
    else:
        # 开发环境
        return f"{username}_todo_data.db"

def encrypt_password(password):
    """简单加密密码"""
    # 创建一个密钥（使用机器码作为部分种子）
    key = get_machine_code() + "TodoApp_Secret_Key"
    # 对明文密码和密钥进行结合
    combined = ""
    for i in range(len(password)):
        combined += password[i] + key[i % len(key)]
    # Base64编码
    encrypted = base64.b64encode(combined.encode()).decode()
    return encrypted

def decrypt_password(encrypted_password):
    """解密密码"""
    try:
        # Base64解码
        key = get_machine_code() + "TodoApp_Secret_Key"
        decoded = base64.b64decode(encrypted_password.encode()).decode()
        # 还原密码（从组合字符串中提取原始密码）
        password = ""
        for i in range(0, len(decoded), 2):
            if i < len(decoded):
                password += decoded[i]
        return password
    except Exception as e:
        print(f"解密密码时出错: {e}")
        return ""

class WebDAVWorker(QThread):
    """WebDAV操作的工作线程"""
    progress_signal = pyqtSignal(int)
    finished_signal = pyqtSignal(bool, str)
    log_signal = pyqtSignal(str)
    
    def __init__(self, url, username, password, local_file, operation, remote_path=""):
        super().__init__()
        self.url = url.rstrip('/')
        self.username = username
        self.password = password
        self.local_file = local_file
        self.operation = operation  # "upload" 或 "download"
        self.remote_path = remote_path.rstrip('/') if remote_path else ""
        
    def run(self):
        try:
            # 设置认证
            auth = (self.username, self.password) if self.username and self.password else None
            
            if self.operation == "test":
                self._test_connection(auth)
            else:
                # 获取文件名
                filename = os.path.basename(self.local_file)
                
                # 构建远程URL - 确保有正确的路径结构
                if self.remote_path:
                    # 如果有指定远程路径，使用它
                    remote_url = urljoin(self.url, f"{self.remote_path}/{filename}")
                else:
                    # 如果没有指定远程路径，使用默认路径
                    remote_url = urljoin(self.url, f"todo_backup/{filename}")
                
                if self.operation == "upload":
                    self._upload_file(auth, remote_url)
                elif self.operation == "download":
                    self._download_file(auth, remote_url)
                
        except Exception as e:
            self.finished_signal.emit(False, f"WebDAV操作失败: {str(e)}")
    
    def _upload_file(self, auth, remote_url):
        """上传文件"""
        try:
            # 检查本地文件是否存在
            if not os.path.exists(self.local_file):
                self.finished_signal.emit(False, "本地文件不存在")
                return
            
            # 读取文件内容
            with open(self.local_file, 'rb') as f:
                file_content = f.read()
            
            # 设置请求头 - 针对坚果云等WebDAV服务优化
            headers = {
                'Content-Type': 'application/octet-stream',
                'Content-Length': str(len(file_content)),
                'User-Agent': 'TodoApp/2.1.2'
            }
            
            # 确保目录存在
            dir_url = os.path.dirname(remote_url)
            if dir_url != self.url:
                try:
                    # 尝试创建目录
                    mkcol_response = requests.request('MKCOL', dir_url, auth=auth, timeout=10)
                    if mkcol_response.status_code not in [200, 201, 204, 405]:  # 405表示目录已存在
                        # 如果创建失败，尝试创建父目录
                        parent_dir = os.path.dirname(dir_url)
                        if parent_dir != self.url:
                            parent_response = requests.request('MKCOL', parent_dir, auth=auth, timeout=10)
                            if parent_response.status_code in [200, 201, 204, 405]:
                                # 再次尝试创建目标目录
                                mkcol_response = requests.request('MKCOL', dir_url, auth=auth, timeout=10)
                except Exception as e:
                    pass  # 忽略目录创建错误，继续尝试上传
            
            # 上传文件
            response = requests.put(remote_url, data=file_content, headers=headers, auth=auth, timeout=30)
            
            if response.status_code in [200, 201, 204]:
                self.progress_signal.emit(100)
                self.finished_signal.emit(True, "文件上传成功")
            else:
                # 尝试获取更详细的错误信息
                error_msg = f"上传失败，HTTP状态码: {response.status_code}"
                try:
                    if response.text:
                        error_msg += f"，错误信息: {response.text[:200]}"
                except:
                    pass
                self.finished_signal.emit(False, error_msg)
                
        except requests.exceptions.RequestException as e:
            self.finished_signal.emit(False, f"网络请求失败: {str(e)}")
        except Exception as e:
            self.finished_signal.emit(False, f"上传文件时出错: {str(e)}")
    
    def _download_file(self, auth, remote_url):
        """下载文件"""
        try:
            # 设置请求头
            headers = {
                'User-Agent': 'TodoApp/2.1.2'
            }
            
            # 下载文件
            response = requests.get(remote_url, auth=auth, timeout=30, stream=True, headers=headers)
            
            if response.status_code == 200:
                # 获取文件大小
                total_size = int(response.headers.get('content-length', 0))
                downloaded_size = 0
                
                # 写入本地文件
                with open(self.local_file, 'wb') as f:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                            downloaded_size += len(chunk)
                            if total_size > 0:
                                progress = int((downloaded_size / total_size) * 100)
                                self.progress_signal.emit(progress)
                
                self.progress_signal.emit(100)
                self.finished_signal.emit(True, "文件下载成功")
            else:
                # 尝试获取更详细的错误信息
                error_msg = f"下载失败，HTTP状态码: {response.status_code}"
                try:
                    if response.text:
                        error_msg += f"，错误信息: {response.text[:200]}"
                except:
                    pass
                self.finished_signal.emit(False, error_msg)
                
        except requests.exceptions.RequestException as e:
            self.finished_signal.emit(False, f"网络请求失败: {str(e)}")
        except Exception as e:
            self.finished_signal.emit(False, f"下载文件时出错: {str(e)}")
    
    def _test_connection(self, auth):
        """测试连接"""
        try:
            # 使用OPTIONS方法测试WebDAV支持
            headers = {
                'Depth': '0',
                'Content-Type': 'application/xml'
            }
            
            # 尝试OPTIONS请求来检查WebDAV支持
            response = requests.options(self.url, auth=auth, timeout=10, headers=headers)
            
            if response.status_code in [200, 207]:
                self.finished_signal.emit(True, "WebDAV连接测试成功")
            else:
                # 如果OPTIONS失败，尝试GET请求
                response = requests.get(self.url, auth=auth, timeout=10)
                if response.status_code in [200, 207]:
                    self.finished_signal.emit(True, "WebDAV连接测试成功")
                else:
                    self.finished_signal.emit(False, f"连接测试失败，HTTP状态码: {response.status_code}")
                
        except requests.exceptions.RequestException as e:
            self.finished_signal.emit(False, f"连接测试失败: {str(e)}")
        except Exception as e:
            self.finished_signal.emit(False, f"连接测试时出错: {str(e)}")

class WebDAVSettingsDialog(QDialog):
    """WebDAV云设置对话框"""
    # 添加信号
    height_setting_changed = pyqtSignal()
    
    def __init__(self, parent=None, db_file=None):
        super().__init__(parent)
        self.db_file = db_file
        self.setWindowTitle("设置")
        self.setMinimumWidth(500)
        self.setMinimumHeight(400)
        
        # 创建主布局
        main_layout = QVBoxLayout(self)
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)
        
        # 服务器设置组
        server_group = QGroupBox()
        server_layout = QFormLayout()
        server_layout.setSpacing(10)
        
        # WebDAV URL
        self.url_input = QLineEdit()
        self.url_input.setPlaceholderText("https://your-webdav-server.com/dav/")
        server_layout.addRow("WebDAV URL:", self.url_input)
        
        # 用户名
        self.username_input = QLineEdit()
        self.username_input.setPlaceholderText("用户名")
        server_layout.addRow("用户名:", self.username_input)
        
        # 密码
        self.password_input = QLineEdit()
        self.password_input.setEchoMode(QLineEdit.Password)
        self.password_input.setPlaceholderText("密码")
        server_layout.addRow("密码:", self.password_input)
        
        # 远程路径
        self.remote_path_input = QLineEdit()
        self.remote_path_input.setPlaceholderText("todo_backup (可选)")
        server_layout.addRow("远程路径:", self.remote_path_input)
        
        server_group.setLayout(server_layout)
        main_layout.addWidget(server_group)
        
        # 待办信息高度设置组
        height_group = QGroupBox("待办信息高度设置")
        height_layout = QFormLayout()
        height_layout.setSpacing(10)
        
        # 待办信息高度下拉选择框
        self.height_combo = QComboBox()
        self.height_combo.addItem("默认高度")
        self.height_combo.addItem("自适应高度")
        self.height_combo.addItem("精简高度（一个汉字高度）")
        height_layout.addRow("", self.height_combo)
        
        # 高度说明标签
        height_note = QLabel("设置待办事项在界面中显示的高度，影响整体布局")
        height_note.setStyleSheet("color: #666666; font-size: 8pt;")
        height_layout.addRow("", height_note)
        
        height_group.setLayout(height_layout)
        main_layout.addWidget(height_group)
        
        # 操作按钮组
        operation_group = QGroupBox("测试操作")
        operation_layout = QVBoxLayout()
        
        # 测试连接按钮
        test_button = QPushButton("测试连接")
        test_button.clicked.connect(self.test_connection)
        operation_layout.addWidget(test_button)
        
        operation_group.setLayout(operation_layout)
        main_layout.addWidget(operation_group)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        
        # 保存设置按钮
        save_button = QPushButton("保存设置")
        save_button.clicked.connect(self.save_settings)
        button_layout.addWidget(save_button)
        
        # 关闭按钮
        close_button = QPushButton("关闭")
        close_button.clicked.connect(self.close)
        button_layout.addWidget(close_button)
        
        main_layout.addLayout(button_layout)
        
        # 加载保存的设置
        self.load_settings()
        
        # 设置样式
        self.setStyleSheet("""
            QDialog {
                font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
                font-size: 9pt;
            }
            QGroupBox {
                font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
                font-weight: bold;
                font-size: 9pt;
                border: 1px solid #cccccc;
                border-radius: 5px;
                margin-top: 10px;
                padding-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px 0 5px;
                font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
                font-weight: bold;
                font-size: 9pt;
            }
            QLabel {
                font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
                font-size: 9pt;
            }
            QLineEdit {
                font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
                font-size: 9pt;
                padding: 5px;
                border: 1px solid #cccccc;
                border-radius: 3px;
            }
            QPushButton {
                font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
                font-size: 9pt;
                font-weight: bold;
                padding: 8px 15px;
                background-color: #4CAF50;
                color: white;
                border: none;
                border-radius: 3px;
            }
            QPushButton:hover {
                background-color: #45a049;
            }
            QPushButton:pressed {
                background-color: #3d8b40;
            }
        """)
    
    def load_settings(self):
        """加载保存的设置"""
        settings = QSettings("TodoApp", "WebDAVSync")
        self.url_input.setText(settings.value('url', ''))
        self.username_input.setText(settings.value('username', ''))
        
        # 解密密码
        encrypted_password = settings.value('password', '')
        if encrypted_password:
            self.password_input.setText(decrypt_password(encrypted_password))
        
        self.remote_path_input.setText(settings.value('remote_path', ''))
        
        # 加载高度设置
        height_value = settings.value('todo_height', '默认高度')
        if height_value == "自适应高度":
            self.height_combo.setCurrentIndex(1)
        elif height_value == "精简高度（一个汉字高度）":
            self.height_combo.setCurrentIndex(2)
        else:
            self.height_combo.setCurrentIndex(0)
    
    def save_settings(self):
        """保存设置"""
        url = self.url_input.text().strip()
        username = self.username_input.text().strip()
        password = self.password_input.text()
        remote_path = self.remote_path_input.text().strip()
        
        if not url:
            QMessageBox.warning(self, "错误", "请输入WebDAV URL")
            return
        
        # 保存设置
        settings = QSettings("TodoApp", "WebDAVSync")
        settings.setValue('url', url)
        settings.setValue('username', username)
        settings.setValue('password', encrypt_password(password))
        settings.setValue('remote_path', remote_path)
        
        # 保存高度设置
        height_value = self.height_combo.currentText()
        settings.setValue('todo_height', height_value)
        
        # 发送高度设置改变信号
        self.height_setting_changed.emit()
        
        QMessageBox.information(self, "成功", "设置已保存")
    
    def test_connection(self):
        """测试WebDAV连接"""
        url = self.url_input.text().strip()
        username = self.username_input.text().strip()
        password = self.password_input.text()
        
        if not url:
            QMessageBox.warning(self, "错误", "请输入WebDAV URL")
            return
        
        try:
            # 创建工作线程
            self.worker = WebDAVWorker(url, username, password, "", "test")
            self.worker.finished_signal.connect(self.on_test_finished)
            self.worker.start()
        except Exception as e:
            QMessageBox.warning(self, "错误", f"测试连接失败: {str(e)}")
    
    def on_test_finished(self, success, message):
        """测试连接完成回调"""
        if success:
            QMessageBox.information(self, "成功", "WebDAV连接测试成功！")
        else:
            QMessageBox.warning(self, "失败", f"WebDAV连接测试失败: {message}")

# 测试代码
if __name__ == "__main__":
    app = QApplication(sys.argv)
    dialog = WebDAVSettingsDialog(db_file="test.db")
    dialog.show()
    sys.exit(app.exec_())